package io.xccit.common.utils;

import io.minio.*;
import io.minio.errors.MinioException;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import io.xccit.common.exception.ServiceException;
import io.xccit.common.exception.file.FileUploadException;
import io.xccit.common.utils.uuid.UUID;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;

/**
 * @author ywx
 * @version 1.0
 * @create 2025/7/16
 * <p>Minio工具类</p>
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@Component
public class MinIOUtils {

    @Value("${minio.bucketName}")
    private String bucketName;
    @Value("${minio.endpoint}")
    private String endpoint;
    //返回的图片路径
    private String url;
    @Autowired
    private MinioClient minioClient;

    /**
     * 判断桶是否存在,如果存在返回true,如果不存在返回false
     *
     * @param bucketName
     * @return
     */
    @SneakyThrows
    public Boolean existBucket(String bucketName) {
        boolean exist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        if (!exist) {
            return false;
        }
        return true;
    }

    /**
     * 创建桶
     *
     * @param bucketName
     * @return
     */
    @SneakyThrows
    public void makeBucket(String bucketName) {
        minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
    }

    /**
     * 删除桶（需为空）
     */
    @SneakyThrows
    public void removeBucket(String bucketName) {
        minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
    }

    /**
     * 列出所有桶
     */
    @SneakyThrows
    public List<Bucket> listBuckets() {
        return minioClient.listBuckets();
    }

    /**
     * 列出桶中的对象
     */
    @SneakyThrows
    public List<Item> listObjects(String bucketName, String prefix, boolean recursive) {
        List<Item> items = new ArrayList<>();
        Iterable<Result<Item>> results = minioClient.listObjects(
                ListObjectsArgs.builder()
                        .bucket(bucketName)
                        .prefix(prefix == null ? "" : prefix)
                        .recursive(recursive)
                        .build());
        for (Result<Item> result : results) {
            items.add(result.get());
        }
        return items;
    }

    /**
     * 上传对象
     *
     * @param objectName
     * @param file
     * @return
     * @throws Exception
     */
    @SneakyThrows
    public void putObject(String objectName, MultipartFile file) {

        //判断 桶是否存在
        boolean flag = existBucket(bucketName);

        if (flag) {
            PutObjectArgs args = PutObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .contentType(file.getContentType())
                    .stream(file.getInputStream(), file.getSize(), -1)
                    .build();
            minioClient.putObject(args);
        }
    }

    /**
     * 上传对象到指定桶
     */
    @SneakyThrows
    public void putObject(String bucketName, String objectName, MultipartFile file) {
        boolean flag = existBucket(bucketName);
        if (flag) {
            PutObjectArgs args = PutObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .contentType(file.getContentType())
                    .stream(file.getInputStream(), file.getSize(), -1)
                    .build();
            minioClient.putObject(args);
        }
    }

    /**
     * 流式上传文件
     * @param file
     * @return
     */
    public String upload(MultipartFile file) throws FileUploadException {
        InputStream inputStream = null;
        try {
            existBucket(bucketName);
            //TODO 流式文件上传
            String uuid = UUID.randomUUID().toString();
            uuid = uuid.replaceAll("-", "");
            //TODO 防止文件名重复+日期文件夹分类
            String dateDir = DateUtils.dateTime();
            String filename = dateDir + "/" + uuid + file.getOriginalFilename();
            inputStream = file.getInputStream();
            long size = file.getSize();
            minioClient.putObject(
                    PutObjectArgs.builder().bucket(bucketName)
                            .object(filename)
                            .stream(inputStream, size, -1)
                            .build());
            //TODO 构建文件路径
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.append(endpoint);
            stringBuilder.append(bucketName);
            stringBuilder.append("/");
            stringBuilder.append(filename);
            url = stringBuilder.toString();
        } catch (MinioException e) {
            e.printStackTrace();
            throw new FileUploadException("文件上传失败");
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        } catch (InvalidKeyException e) {
            throw new RuntimeException(e);
        } finally {
            if (inputStream != null){
                try {
                    inputStream.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
        return url;
    }

    /**
     * 删除对象
     *
     * @param objectName
     * @return
     * @throws Exception
     */
    @SneakyThrows
    public boolean removeObject(String objectName) {
        boolean flag = existBucket(bucketName);
        if (flag) {
            RemoveObjectArgs args = RemoveObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .build();
            minioClient.removeObject(args);
            return true;
        }
        return false;
    }

    /**
     * 从指定桶删除对象
     */
    @SneakyThrows
    public boolean removeObject(String bucketName, String objectName) {
        boolean flag = existBucket(bucketName);
        if (flag) {
            RemoveObjectArgs args = RemoveObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .build();
            minioClient.removeObject(args);
            return true;
        }
        return false;
    }

    /**
     * 获取对象信息
     *
     * @param objectName
     * @return
     * @throws Exception
     */
    @SneakyThrows
    public ObjectStat getMessage(String objectName) {
        boolean flag = existBucket(bucketName);
        if (flag) {
            ObjectStat statObject = minioClient.statObject(StatObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .build());
            return statObject;
        }
        return null;
    }

    /**
     * 返回的文件路径,不会过期.
     *
     * @param objectName
     * @return
     */
    @SneakyThrows
    public String getObjectUrl(String objectName) {
        Boolean flag = existBucket(bucketName);
        String url = "";
        if (flag) {
            url = minioClient.getObjectUrl(bucketName, objectName);

        }
        return url;
    }

    /**
     * 获取指定桶对象的URL（非签名，适用于公开桶）
     */
    @SneakyThrows
    public String getObjectUrl(String bucketName, String objectName) {
        Boolean flag = existBucket(bucketName);
        String url = "";
        if (flag) {
            url = minioClient.getObjectUrl(bucketName, objectName);
        }
        return url;
    }

    /**
     * 获取预签名URL
     */
    @SneakyThrows
    public String getPresignedUrl(String bucketName, String objectName, int expiresSeconds) {
        if (expiresSeconds <= 0) {
            expiresSeconds = (int) Duration.ofHours(1).getSeconds();
        }
        return minioClient.getPresignedObjectUrl(
                GetPresignedObjectUrlArgs.builder()
                        .method(Method.GET)
                        .bucket(bucketName)
                        .object(objectName)
                        .expiry(expiresSeconds)
                        .build());
    }

    /**
     * 下载
     *
     * @param filename
     * @param response
     */
    public void getObject(String filename, HttpServletResponse response) {
        InputStream in = null;
        OutputStream out = null;
        try {
            in = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(filename).build());
            int length = 0;
            byte[] buffer = new byte[1024];
            out = response.getOutputStream();
            response.reset();
            response.addHeader("Content-Disposition",
                    " attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));
            response.setContentType("application/octet-stream");
            while ((length = in.read(buffer)) > 0) {
                out.write(buffer, 0, length);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
